package com.example.udpmulticastdemo;

import android.os.AsyncTask;
import android.util.Log;
import android.util.Pair;


import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.util.ArrayList;
import java.util.HashMap;

public class MulReceiverAsyncTask extends AsyncTask<Void, Void, Void> {
    private static final String TAG = "MulticastReceiver";
    private final String mGroupIp;
    private final int mGroupPort;
    private boolean mIntercept=false;
    public MulReceiverAsyncTask(String mGroupIp, int mGroupPort) {
        this.mGroupIp = mGroupIp;
        this.mGroupPort = mGroupPort;
    }

    @Override
    protected Void doInBackground(Void... params) {
        try {
            InetAddress group = InetAddress.getByName(mGroupIp);
            MulticastSocket socket = new MulticastSocket(mGroupPort);
            socket.joinGroup(group);

            Log.e(TAG, "Multicast Receiver Started   group:"+group.getHostAddress()+"   port:"+mGroupPort);
            byte[] buffer = new byte[1024*5];
            while (!mIntercept) {
//                Log.e(TAG, "等待消息中...");
                DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

                socket.receive(packet);
////                Log.e(TAG, "收到新消息...");
//                //解析包头
                if (!checkUDPFecHeader(packet.getData(),packet.getLength())) {
//                    Log.e(TAG, "不处理这条消息...");
                    continue;
                }
//                // 发送消息或数据到子线程
//                mUdpDataProcessThread.postUdpData(packet.getData(),packet.getLength());
//                Log.e(TAG, "Java层消息传输完成... "+Thread.currentThread());
            }
        } catch (IOException e) {
            Log.e(TAG, "doInBackground:  catch"+e);
            e.printStackTrace();
        }finally {
            Log.e(TAG, "doInBackground:  finally");
        }
        return null;
    }

    /**
     *
     * @param data
     * @param length
     * @return 当前屏幕广播视频流是否需要处理 true:需要处理并渲染   false：不处理，丢弃。
     */
    private boolean checkUDPFecHeader(byte[] data, int length) {
        if (length<8) return false;
        //是否与当前加入的屏幕广播的sessionId一致
        //获取SessionID
        byte[] iSessionIdBytes = new byte[8];
        System.arraycopy(data, 17, iSessionIdBytes, 0, 8);
        long iSessionId = BytesUtil.byteToLong(iSessionIdBytes);
//        Log.e(TAG, "printUDPFecHeader:   iSessionId:"+iSessionId+"     currentUlSessionId:"+ScreenBroadcastManager.currentUlSessionId);
//        if (iSessionId != ScreenBroadcastManager.currentUlSessionId){
//            return false;
//        }

        //获取 usTcpSeqNo 表示第几组
        byte[] tempBytes = new byte[4];
        tempBytes[0]=data[15];
        tempBytes[1]=data[16];
        tempBytes[2]=0;
        tempBytes[3]=0;
        int usTcpSeqNo = BytesUtil.byteToInt(tempBytes);
        //获取 usUdpSeqNo 表示所在组里的第几个包。
        tempBytes[0]=data[9];
        tempBytes[1]=data[10];
        tempBytes[2]=0;
        tempBytes[3]=0;
        int usUdpSeqNo = BytesUtil.byteToInt(tempBytes);
        //获取 m_uFecDataPktsNum
        tempBytes[0]=data[29];
        tempBytes[1]=data[30];
        tempBytes[2]=0;
        tempBytes[3]=0;
        int m_uFecDataPktsNum = BytesUtil.byteToInt(tempBytes);
        //获取 uFecRepairPktsNum
        tempBytes[0]=data[31];
        tempBytes[1]=data[32];
        tempBytes[2]=0;
        tempBytes[3]=0;
        int uFecRepairPktsNum = BytesUtil.byteToInt(tempBytes);
        Log.e(TAG, "checkUDPFecHeader  printUDPFecHeader   收到信息，已解析-->   usTcpSeqNo:"+usTcpSeqNo+"  usUdpSeqNo:"+usUdpSeqNo+"  m_uFecDataPktsNum:"+m_uFecDataPktsNum+"  uFecRepairPktsNum:"+uFecRepairPktsNum);
        if (mLastUDPInfoBean==null){
            //表示刚收到第一个包，不算丢包

        }else {
            //此处开始计算丢包
            // TODO: 2024/1/30  每100组丢多少组(就是这一组一个包都没收到的情况)
            if (usTcpSeqNo>mLastUDPInfoBean.getUsTcpSeqNo()+1){
                mHasDiceTcpCount += (usTcpSeqNo-mLastUDPInfoBean.getUsTcpSeqNo()-1);
            }else if (usTcpSeqNo<mLastUDPInfoBean.getUsTcpSeqNo()){
                mHasDiceTcpCount +=(Short.MAX_VALUE-mLastUDPInfoBean.getUsTcpSeqNo());
                mHasDiceTcpCount +=usTcpSeqNo;
            }

            // TODO: 2024/1/30  每1000个包丢多少包(无法统计丢失一整组的数据包)
            int tempCount=0;
            if (usTcpSeqNo==mLastUDPInfoBean.getUsTcpSeqNo()){
                //同组
                tempCount += (usUdpSeqNo-mLastUDPInfoBean.getUsUdpSeqNo()-1);
                mLastDiceUdpCount+=tempCount;
            }else {
                //不同组
                tempCount += (mLastUDPInfoBean.getM_uFecDataPktsNum()+mLastUDPInfoBean.getuFecRepairPktsNum()-mLastUDPInfoBean.getUsUdpSeqNo()-1);
                mLastDiceUdpCount+=tempCount;
                tempCount += usUdpSeqNo;
            }
            mHasDiceUdpCount += tempCount;
            mNumberOfStatisticalPackages+=tempCount;

            // TODO: 2024/1/30 统计每组的丢包个数，是否会影响数据包的恢复
            int totalPktsNum = mLastUDPInfoBean.getuFecRepairPktsNum() + mLastUDPInfoBean.getM_uFecDataPktsNum();
            if (mLastDiceUdpCount>mLastUDPInfoBean.getuFecRepairPktsNum() && mLastDIceTcpSeqNo!=usTcpSeqNo) {
                //超出了冗余包个数，无法恢复。
                //统计个数
                mHasNotRestoreCount++;
                //避免重复统计
                mLastDIceTcpSeqNo=usTcpSeqNo;
            }

            if (usTcpSeqNo!=mLastUDPInfoBean.getUsTcpSeqNo()){
                if (mLastDiceUdpCount>mLastUDPInfoBean.getuFecRepairPktsNum()){
                    //这里可以得到上一次的丢包数据,存入
                    mHasNotRestoreInfoList.add(new Pair<>(mLastUDPInfoBean,mLastDiceUdpCount));
                }
                mLastDiceUdpCount=usUdpSeqNo;
            }

            // TODO: 2024/1/30  其它


        }
        mLastUDPInfoBean=new UDPInfoBean(usTcpSeqNo,usUdpSeqNo,m_uFecDataPktsNum,uFecRepairPktsNum);

        //每一百组丢包情况
        if (mStartTcpSeqNo<0){
            mStartTcpSeqNo=usTcpSeqNo;
        }else {
            if (usTcpSeqNo-mStartTcpSeqNo>=100) {
                mStartTcpSeqNo=-1;
                mDiceList.add(mHasDiceTcpCount);
                StringBuilder stringBuilder = new StringBuilder();
                double totalCount=0.0;
                for (int i = 0; i < mDiceList.size(); i++) {
                    stringBuilder.append(mDiceList.get(i)+",");
                    totalCount+=mDiceList.get(i);
                }
                Log.e(TAG, "checkUDPFecHeader:  每100组丢完整组数:"+mHasDiceTcpCount+" 平均每100组数据丢包的平均组数："+(totalCount/((double)mDiceList.size()))+"  打印每100组数据丢包的组数详情："+stringBuilder);
                //重置
                mHasDiceTcpCount =0;
                mStartTcpSeqNo=-1;
            }
        }

        //每1000个包丢包数量情况
        mNumberOfStatisticalPackages++;
        if (mNumberOfStatisticalPackages>=1000){
            //统计到了1000个包,打印数据
            mDiceListForUdp.add(mHasDiceUdpCount);
            StringBuilder stringBuilder = new StringBuilder();
            double totalUdpCount=0;
            for (int i = 0; i < mDiceListForUdp.size(); i++) {
                stringBuilder.append(mDiceListForUdp.get(i)+",");
                totalUdpCount+=mDiceListForUdp.get(i);
            }
            Log.e(TAG, "checkUDPFecHeader:  每1000个数据包丢包数量："+mHasDiceUdpCount+" 打印每1000个数据包，丢包的平均个数："+totalUdpCount/(double)mDiceListForUdp.size()+"  打印每1000个数据包，丢包的个数："+stringBuilder);

            //统计1000个包的丢包导致组无法恢复的数据
            Pair<Double,UDPInfoBean> maxPacketLossRate=new Pair<>(0.0,new UDPInfoBean());
            Pair<Integer,UDPInfoBean> maxPacketLossNum=new Pair<>(0,new UDPInfoBean());
            Pair<Integer,UDPInfoBean> maxPacketLossOffset=new Pair<>(0,new UDPInfoBean());
            int totalPackNums=0;
            int totalLosePackNums=0;
            int totalLosePackOffset=0;
            StringBuilder stringBuilder1 = new StringBuilder();
            for (int i = 0; i < mHasNotRestoreInfoList.size(); i++) {
                Pair<UDPInfoBean, Integer> item = mHasNotRestoreInfoList.get(i);
                double itemPacketLossRate = ((double) item.second) / (double) (item.first.getuFecRepairPktsNum() + item.first.getM_uFecDataPktsNum());
                stringBuilder1.append("{丢包个数："+item.second+"个,丢包率："+ itemPacketLossRate +",m_uFecDataPktsNum="+item.first.m_uFecDataPktsNum+",uFecRepairPktsNum="+item.first.uFecRepairPktsNum
                        +",usTcpSeqNo="+item.first.usTcpSeqNo+"}，  ");

                //统计一波丢包数据。
                totalPackNums+=(item.first.getM_uFecDataPktsNum()+item.first.getuFecRepairPktsNum());
                totalLosePackNums+=item.second;
                totalLosePackOffset+=item.second-item.first.getuFecRepairPktsNum();
                /**
                 * 丢包率 = 丢包数 除以  (数据包数+校验包数之和）
                 * ①丢包组的平均丢包率。
                 * ②丢包组中，丢包率最高的情况。
                 *
                 * ③丢包组中，丢包数最多的情况。
                 *
                 * ⑤丢包组中，丢包数-校验包数的差值。取平均值。
                 * ⑥丢包组中，丢包数-校验包数的差值。取最大值。
                 */
                //②丢包组中，丢包率最高的情况。
                if (itemPacketLossRate> maxPacketLossRate.first){
                    maxPacketLossRate=new Pair<>(itemPacketLossRate,item.first);
                }
                //③丢包组中，丢包数最多的情况。
                if (item.second > maxPacketLossNum.first){
                    maxPacketLossNum=new Pair<>(item.second,item.first);
                }
                //⑥丢包组中，丢包数-校验包数的差值。取最大值。
                if (item.second-item.first.uFecRepairPktsNum >maxPacketLossOffset.first){
                    maxPacketLossOffset=new Pair<>(item.second-item.first.uFecRepairPktsNum,item.first);
                }

            }
            //①丢包组的平均丢包率。
            double averagePacketLossRate = 0;
            if (totalLosePackNums>0 && totalPackNums>0){
                averagePacketLossRate=((double) totalLosePackNums) / ((double)totalPackNums);
            }
            //④丢包组中，平均丢包数。
            double averagePacketLossCount = 0;
            if (totalLosePackNums>0 && mHasNotRestoreInfoList.size()>0) {
                averagePacketLossCount=((double)totalLosePackNums) / ((double)mHasNotRestoreInfoList.size());
            }

            //⑤丢包组中，丢包数-校验包数的差值。取平均值。
            double averagePacketLossOffset = 0;
            if (totalLosePackOffset>0 && mHasNotRestoreInfoList.size()>0){
                averagePacketLossOffset=((double)totalLosePackOffset) / ((double)mHasNotRestoreInfoList.size());
            }
            if (mHasNotRestoreInfoList.size()>0) {
                Log.e(TAG, "checkUDPFecHeader:  每1000个数据包，无法恢复的组数:" + mHasNotRestoreInfoList.size() + "组" +
                        "\n  ①平均丢包率为:" + averagePacketLossRate +
                        "\n  ②最高丢包率为：" + maxPacketLossRate.first + " 最高丢包率详情：" + maxPacketLossRate.second.toString() +
                        "\n  ③丢包组中，丢包数最多的情况,丢包" + maxPacketLossNum.first + "个," + "详情：" + maxPacketLossNum.second.toString() +
                        "\n  ④丢包组中，平均丢包数:" + averagePacketLossCount +
                        "\n  ⑤丢包组中，丢包数-校验包数的差值。取平均值:" + averagePacketLossOffset +
                        "\n  ⑥丢包组中，丢包数-校验包数的差值。取最大值:" + maxPacketLossOffset.first + " 详情：" + maxPacketLossOffset.second.toString() +
                        "\n  无法恢复的组数详情：  " + stringBuilder1);
            }else {
                Log.e(TAG, "checkUDPFecHeader:  每1000个数据包，无法恢复的组数:" + mHasNotRestoreInfoList.size() + "组");
            }
            // TODO: 2024/1/30 打印平均每次的1000个数据包，丢包的平均数

            mHasNotRestoreCount=0;
            mLastDiceUdpCount=0;
            mLastDIceTcpSeqNo=-1;
            mHasNotRestoreInfoList.clear();

            mNumberOfStatisticalPackages=0;
            mHasDiceUdpCount=0;
        }
        return true;
    }
    //每100组丢多少组
    private int mStartTcpSeqNo=-1;
    private int mHasDiceTcpCount =0;
    private ArrayList<Integer> mDiceList=new ArrayList<>();

    //每1000个包丢多少包
    private int mNumberOfStatisticalPackages=0;//已经统计了多少个包
    private int mHasDiceUdpCount =0;
    private ArrayList<Integer> mDiceListForUdp=new ArrayList<>();

    //统计每组的丢包个数，是否会影响数据包的恢复 （每1000个数据包下，无法恢复的数据组数。）
    private int mHasNotRestoreCount=0;
    private int mLastDiceUdpCount=0;//上一个组丢失的数据包数量。
    private ArrayList<Pair<UDPInfoBean,Integer>> mHasNotRestoreInfoList=new ArrayList<>();
    private int mLastDIceTcpSeqNo=-1;

    //分析一下这些无法恢复的组， 这些组每组总共多少包？丢了多少包？是不是包太少导致？
    private HashMap<Integer,Integer> mMap=new HashMap<>();



    private UDPInfoBean mLastUDPInfoBean=null;
    private class UDPInfoBean{
        private int usTcpSeqNo;
        private int usUdpSeqNo;
        private int m_uFecDataPktsNum;
        private int uFecRepairPktsNum;

        public UDPInfoBean() {
        }

        public UDPInfoBean(int usTcpSeqNo, int usUdpSeqNo, int m_uFecDataPktsNum, int uFecRepairPktsNum) {
            this.usTcpSeqNo = usTcpSeqNo;
            this.usUdpSeqNo = usUdpSeqNo;
            this.m_uFecDataPktsNum = m_uFecDataPktsNum;
            this.uFecRepairPktsNum = uFecRepairPktsNum;
        }

        public int getUsTcpSeqNo() {
            return usTcpSeqNo;
        }

        public void setUsTcpSeqNo(int usTcpSeqNo) {
            this.usTcpSeqNo = usTcpSeqNo;
        }

        public int getUsUdpSeqNo() {
            return usUdpSeqNo;
        }

        public void setUsUdpSeqNo(int usUdpSeqNo) {
            this.usUdpSeqNo = usUdpSeqNo;
        }

        public int getM_uFecDataPktsNum() {
            return m_uFecDataPktsNum;
        }

        public void setM_uFecDataPktsNum(int m_uFecDataPktsNum) {
            this.m_uFecDataPktsNum = m_uFecDataPktsNum;
        }

        public int getuFecRepairPktsNum() {
            return uFecRepairPktsNum;
        }

        public void setuFecRepairPktsNum(int uFecRepairPktsNum) {
            this.uFecRepairPktsNum = uFecRepairPktsNum;
        }

        @Override
        public String toString() {
            return "UDPInfoBean{" +
                    "usTcpSeqNo=" + usTcpSeqNo +
                    ", m_uFecDataPktsNum=" + m_uFecDataPktsNum +
                    ", uFecRepairPktsNum=" + uFecRepairPktsNum +
                    '}';
        }
    }


    public void stopTask(){
        mIntercept=true;
        cancel(true);
    }

    public boolean ismIntercept() {
        return mIntercept;
    }
}
